#
#    Pixel War (Server) - Server de Juego Multiplayer Online
#    Copyright (C) 2010 - Francisco Rivera
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# La clase protocolo es la encargada de procesar los mensajes que llegan
# desde los clientes, y asociarlos con un handler para ejecutar su accion.
# El protocolo envia los datos necesarios al handler para ejecutar la accion
# pero no los valida. Todas las validaciones se hacen dentro de los handlers
# Cada handler es especificos de cada tipo de paquete que se recibe en el servidor.
# Tambien es la interfaz que usa el servidor para enviarle paquetes a los clientes.

from enviar import Enviar, EnviarTodos
from handlers import HandlerCriaturas, HandlerBala

class Protocolo:

    SEPARADOR = '|'       
    
    ## servidor <-- cliente
    
    def __init__(self, mensaje, socket, id):
        self.hcriat = HandlerCriaturas()
        # separamos el mensaje en un array
        mensaje = mensaje.split(self.SEPARADOR)
        # la accion siempre se encuentra en la primer posicion del array
        accion = mensaje[0]
        
        ## paquetes log-in        
        if accion == 'dt':
            # este paquete se recibe cuando un cliente responde al pedido de datos en el momento de efectuar la conexion
            equipo = mensaje[1]
            # creamos el jugador
            self.handlerCrearJugadorInicial(socket, id, equipo)
            
        elif accion == 'ok':
            self.handlerJugadorListo(id, socket)
        
        ## paquetes in-game        
        elif accion == 'mv':
            # movimiento
            dir = mensaje[1]
            self.handlerMovimiento(id, dir)
            
        elif accion == 'dp':
            # disparo
            dir = mensaje[1]
            self.handlerDisparo(id, dir)
            
        elif accion == 'rr':
            # ressetear ronda
            HandlerCriaturas().restartRonda()
                     
         ## DEBERIA IR UN PAQUETE DE DESCONEXION ACA TAMBIEN
         
        else:            
            print "Paquete desconocido:", accion
            
    ## servidor --> cliente
    
    @classmethod
    def enviarId(self, socket, id):
        Enviar(socket, 'id', [id])
    
    @classmethod
    def enviarCrearJugador(self, jugador):
        # para crear un jugador en todos los clientes
        datos = jugador.getDatos()
        EnviarTodos('cr', datos)
        
    @classmethod
    def enviarCrearTodosJugadores(self, socket, excepcion):
        hcriat = HandlerCriaturas()
        jugadores = [j for j in hcriat.getJugadores().values() if j != excepcion]
        datos = []
        for j in jugadores:
            datos.extend(j.getDatos())
        # Le agregamos la senia de que a "datos" hay que iterarlo (no uso la enie por el encoding, giles)
        ## esto del 'i-' es re sucio
        datos.insert(0, Enviar.IT1NIVEL)
        Enviar(socket, 'cv', [len(jugadores), datos])

    @classmethod
    def enviarJugadorListo(self, jugador):
        Enviar(jugador.socket, 'ok', [])
        HandlerCriaturas().comenzarRonda()
    
    @classmethod
    def enviarHit(self, jugador, danio):
        EnviarTodos('ht', [jugador.getId(), danio])

    @classmethod
    def enviarMovimiento(self, jugador):
        EnviarTodos('mv', [jugador.getId(), jugador.x, jugador.y])             

    @classmethod
    def enviarError(self, socket, id):
        # 1 -> movimiento imposible
        # 2 -> no hay posiciones libres para generar jugador
        Enviar(socket, 'er', [id])

    @classmethod
    def enviarDisparo(self, id, dir, x, y):
        EnviarTodos('dp', [id, dir, x, y])

    @classmethod
    def enviarMapa(self, socket, mapa):
        sec = mapa.getMapaChar()
        Enviar(socket, 'mp', [sec])
    
    @classmethod
    def enviarEliminarCriatura(self, id):
        # este es para cuando se muere
        EnviarTodos('dl', [id])
        
    @classmethod
    def enviarDesconectarCriatura(self, id):
        # este es para cuando hace log-out
        EnviarTodos('dc', [id])
        
    @classmethod
    def enviarNuevaRonda(self):
        azul, rojo, ronda = HandlerCriaturas().ronda.get()
        EnviarTodos('nr', [azul, rojo, ronda])
        
    @classmethod
    def enviarNuevasPos(self, nuevas_pos):
        # nuevas_pos ya es un array
        EnviarTodos('np', nuevas_pos)
        
    ## handlers paquetes recibidos

    def handlerCrearJugadorInicial(self, socket, id, equipo):
        if validarEquipo(equipo):
            # creamos el jugador
            mapa = self.hcriat.getMapa()
            if equipo == "a":                
                pos = mapa.getAzul()
            else:
                pos = mapa.getRojo()
            # hay lugar
            if pos:
                x, y = pos
                jugador = self.hcriat.crearJugador(id, x, y, equipo, socket)
                # le enviamos el mapa
                self.enviarMapa(socket, mapa)
                # creamos el jugador en todos los clientes
                self.enviarCrearJugador(jugador)
                # despues le enviamos al nuevo cliente todos los jugadores excepto el suyo
                self.enviarCrearTodosJugadores(socket, jugador)
                # y por ultimo el score de las rondas
                self.enviarNuevaRonda()
            # no hay lugar
            else:
                print "Error: Todos los lugares ocupados."
                self.enviarError(socket, 2)
        else:
            print "clase invalida:", equipo

    def handlerJugadorListo(self, id, socket):
        jug = self.hcriat.getCriaturaById(id)
        if jug:
            jug.setListo()
            self.enviarJugadorListo(jug)
        self.hcriat.comenzarRonda()

    def handlerMovimiento(self, id, dir):
        jug = self.hcriat.getCriaturaById(id)
        if jug:
            if not jug.estaBloqueadoMov():
                if jug.estaVivo():
                    if validarDir4(dir):
                        if not MoverCriatura(jug, self.hcriat, dir).movido():
                            self.enviarError(jug.socket, 1)

    def handlerDisparo(self, id, dir):
        jug = self.hcriat.getCriaturaById(id)
        if jug:
            if jug.estaVivo() and not jug.estaBloqueadoDisp():
                if validarDir8(dir):
                    # manejo interno
                    HandlerBala(jug, dir, self)
                    # envio del paquete
                    self.enviarDisparo(id, dir, jug.x, jug.y)


## Acciones que usan el protocolo

class AtacarCriatura:

    def __init__(self, criat_atacante, criat_atacada, danio, hcriat):
        if criat_atacante.esEquipo(criat_atacada.getEquipo()):
            return
        else:
            if criat_atacada.estaVivo():
                criat_atacada.hit(danio)
                Protocolo.enviarHit(criat_atacada, danio)


class MoverCriatura:

    def __init__(self, criatura, hcriat, dir):
        antx, anty = x, y = criatura.getCoor()
        # proximo movimiento
        if dir == 'n':
            y -= 1
        elif dir == 'e':
            x += 1
        elif dir == 's':
            y += 1
        elif dir == 'o':
            x -= 1

        mapa = hcriat.getMapa()
        if not mapa.posBloqueada(x, y):
            if criatura.estaVivo():
                mapa.moverJugador(criatura, x, y)
                Protocolo.enviarMovimiento(criatura)
                self.ok = True
            else:
                self.ok = False
        else:
            self.ok = False

    def movido(self):
        return self.ok


### FUNCIONES PARA VALIDAR

def validarDir4(dir):
    return dir in ('n', 's', 'o', 'e')

def validarDir8(dir):
    return validarDir4(dir) or dir in ('no', 'ne', 'so', 'se')
    
def validarEquipo(equipo):
    return equipo in ("r", "a")   